/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package fr.onevu.gwt.uibinder.rebind;

import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.JTypeParameter;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.TagName;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.DomEvent.Type;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.uibinder.client.UiRenderer;
import com.google.gwt.user.client.ui.IsRenderable;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RenderableStamper;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.beans.Introspector;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import fr.onevu.gwt.uibinder.attributeparsers.AttributeParsers;
import fr.onevu.gwt.uibinder.client.LazyDomElement;
import fr.onevu.gwt.uibinder.client.impl.AbstractUiRenderer;
import fr.onevu.gwt.uibinder.core.shared.impl.StringCase;
import fr.onevu.gwt.uibinder.elementparsers.AttributeMessageParser;
import fr.onevu.gwt.uibinder.elementparsers.BeanParser;
import fr.onevu.gwt.uibinder.elementparsers.ElementParser;
import fr.onevu.gwt.uibinder.elementparsers.IsEmptyParser;
import fr.onevu.gwt.uibinder.elementparsers.UiChildParser;
import fr.onevu.gwt.uibinder.rebind.messages.MessagesWriter;
import fr.onevu.gwt.uibinder.rebind.model.HtmlTemplateMethodWriter;
import fr.onevu.gwt.uibinder.rebind.model.HtmlTemplatesWriter;
import fr.onevu.gwt.uibinder.rebind.model.ImplicitClientBundle;
import fr.onevu.gwt.uibinder.rebind.model.ImplicitCssResource;
import fr.onevu.gwt.uibinder.rebind.model.OwnerClass;
import fr.onevu.gwt.uibinder.rebind.model.OwnerField;

/**
 * Writer for UiBinder generated classes.
 */
public class UiBinderWriter implements Statements {

	private static final String SAFE_VAR_PREFIX = "somethingUnlikelyToCollideWithParamNamesWefio";

	private static final String UI_RENDERER_DISPATCHER_PREFIX = "UiRendererDispatcherFor";

	private static final String PACKAGE_URI_SCHEME = "urn:import:";

	// TODO(rjrjr) Another place that we need a general anonymous field
	// mechanism
	protected static final String CLIENT_BUNDLE_FIELD = "clientBundleFieldNameUnlikelyToCollideWithUserSpecifiedFieldOkay";

	public static String asCommaSeparatedList(String... args) {
		StringBuilder b = new StringBuilder();
		for (String arg : args) {
			if (b.length() > 0) {
				b.append(", ");
			}
			b.append(arg);
		}

		return b.toString();
	}

	/**
	 * Escape text that will be part of a string literal to be interpreted at
	 * runtime as an HTML attribute value.
	 */
	public static String escapeAttributeText(String text) {
		text = escapeText(text, false);

		/*
		 * Escape single-quotes to make them safe to be interpreted at runtime as an
		 * HTML attribute value (for which we by convention use single quotes).
		 */
		text = text.replaceAll("'", "&#39;");
		return text;
	}

	/**
	 * Escape text that will be part of a string literal to be interpreted at
	 * runtime as HTML, optionally preserving whitespace.
	 */
	public static String escapeText(String text, boolean preserveWhitespace) {
		// Replace reserved XML characters with entities. Note that we *don't*
		// replace single- or double-quotes here, because they're safe in text
		// nodes.
		text = text.replaceAll("&", "&amp;");
		text = text.replaceAll("<", "&lt;");
		text = text.replaceAll(">", "&gt;");

		if (!preserveWhitespace) {
			text = text.replaceAll("\\s+", " ");
		}

		return escapeTextForJavaStringLiteral(text);
	}

	/**
	 * Escape characters that would mess up interpretation of this string as a
	 * string literal in generated code (that is, protect \, \n and " ).
	 */
	public static String escapeTextForJavaStringLiteral(String text) {
		text = text.replace("\\", "\\\\");
		text = text.replace("\"", "\\\"");
		text = text.replace("\n", "\\n");

		return text;
	}

	/**
	 * Returns a list of the given type and all its superclasses and implemented
	 * interfaces in a breadth-first traversal.
	 * 
	 * @param type
	 *          the base type
	 * @return a breadth-first collection of its type hierarchy
	 */
	static Iterable<JClassType> getClassHierarchyBreadthFirst(JClassType type) {
		LinkedList<JClassType> list = new LinkedList<JClassType>();
		LinkedList<JClassType> q = new LinkedList<JClassType>();

		q.add(type);
		while (!q.isEmpty()) {
			// Pop the front of the queue and add it to the result list.
			JClassType curType = q.removeFirst();
			list.add(curType);

			// Add implemented interfaces to the back of the queue (breadth first,
			// remember?)
			for (JClassType intf : curType.getImplementedInterfaces()) {
				q.add(intf);
			}
			// Add then add superclasses
			JClassType superClass = curType.getSuperclass();
			if (superClass != null) {
				q.add(superClass);
			}
		}

		return list;
	}

	private static String capitalizePropName(String propName) {
		return StringCase.toUpper(propName.substring(0, 1)) + propName.substring(1);
	}

	/**
	 * Searches for methods named onBrowserEvent in a {@code type}.
	 */
	private static JMethod[] findEventMethods(JClassType type) {
		List<JMethod> methods = new ArrayList<JMethod>(Arrays.asList(type.getInheritableMethods()));

		for (Iterator<JMethod> iterator = methods.iterator(); iterator.hasNext();) {
			JMethod jMethod = iterator.next();
			if (!jMethod.getName().equals("onBrowserEvent")) {
				iterator.remove();
			}
		}

		return methods.toArray(new JMethod[methods.size()]);
	}

	/**
	 * Scan the base class for the getter methods. Assumes getters begin with
	 * "get". See {@link #validateRendererGetters(JClassType)} for a method that
	 * guarantees this method will succeed.
	 */
	private static List<JMethod> findGetterNames(JClassType owner) {
		List<JMethod> ret = new ArrayList<JMethod>();
		for (JMethod jMethod : owner.getInheritableMethods()) {
			String getterName = jMethod.getName();
			if (getterName.startsWith("get")) {
				ret.add(jMethod);
			}
		}
		return ret;
	}

	/**
	 * Scans a class for a method named "render". Returns its parameters except
	 * for the first one. See {@link #validateRenderParameters(JClassType)} for a
	 * method that guarantees this method will succeed.
	 */
	private static JParameter[] findRenderParameters(JClassType owner) {
		JMethod[] methods = owner.getInheritableMethods();
		JMethod renderMethod = null;

		for (JMethod jMethod : methods) {
			if (jMethod.getName().equals("render")) {
				renderMethod = jMethod;
			}
		}

		JParameter[] parameters = renderMethod.getParameters();
		return Arrays.copyOfRange(parameters, 1, parameters.length);
	}

	/**
	 * Finds methods annotated with {@code @UiHandler} in a {@code type}.
	 */
	private static JMethod[] findUiHandlerMethods(JClassType type) {
		ArrayList<JMethod> result = new ArrayList<JMethod>();
		JMethod[] allMethods = type.getInheritableMethods();

		for (JMethod jMethod : allMethods) {
			if (jMethod.getAnnotation(UiHandler.class) != null) {
				result.add(jMethod);
			}
		}

		return result.toArray(new JMethod[result.size()]);
	}

	private static String formatMethodError(JMethod eventMethod) {
		return "\"" + eventMethod.getReadableDeclaration(true, true, true, true, true) + "\"" + " of " + eventMethod.getEnclosingType().getQualifiedSourceName();
	}

	/**
	 * Determine the field name a getter is trying to retrieve. Assumes getters
	 * begin with "get".
	 */
	private static String getterToFieldName(String name) {
		String fieldName = name.substring(3);
		return Introspector.decapitalize(fieldName);
	}

	private static String renderMethodParameters(JParameter[] renderParameters) {
		StringBuilder builder = new StringBuilder();

		for (int i = 0; i < renderParameters.length; i++) {
			JParameter parameter = renderParameters[i];
			builder.append("final ");
			builder.append(parameter.getType().getQualifiedSourceName());
			builder.append(" ");
			builder.append(parameter.getName());
			if (i < renderParameters.length - 1) {
				builder.append(", ");
			}
		}

		return builder.toString();
	}

	private final MortalLogger logger;

	/**
	 * Class names of parsers for various ui types, keyed by the classname of the
	 * UI class they can build.
	 */
	private final Map<String, String> elementParsers = new HashMap<String, String>();

	private final List<String> initStatements = new ArrayList<String>();
	private final List<String> statements = new ArrayList<String>();
	private final HandlerEvaluator handlerEvaluator;
	private final MessagesWriter messages;
	private final DesignTimeUtils designTime;
	private final Tokenator tokenator = new Tokenator();

	private final String templatePath;
	private final TypeOracle oracle;
	/**
	 * The type we have been asked to generated, e.g. MyUiBinder
	 */
	private final JClassType baseClass;

	/**
	 * The name of the class we're creating, e.g. MyUiBinderImpl
	 */
	private final String implClassName;

	private final JClassType uiOwnerType;

	private final JClassType uiRootType;

	private final JClassType isRenderableClassType;

	private final JClassType lazyDomElementClass;

	private final OwnerClass ownerClass;

	private final FieldManager fieldManager;

	private final HtmlTemplatesWriter htmlTemplates;

	private final ImplicitClientBundle bundleClass;

	private final boolean useLazyWidgetBuilders;

	private final boolean useSafeHtmlTemplates;

	private int domId = 0;

	private int fieldIndex;

	private String gwtPrefix;

	private int renderableStamper = 0;

	private String rendered;
	/**
	 * Stack of element variable names that have been attached.
	 */
	private final LinkedList<String> attachSectionElements = new LinkedList<String>();
	/**
	 * Maps from field element name to the temporary attach record variable name.
	 */
	private final Map<String, String> attachedVars = new HashMap<String, String>();

	private int nextAttachVar = 0;
	/**
	 * Stack of statements to be executed after we detach the current attach
	 * section.
	 */
	private final LinkedList<List<String>> detachStatementsStack = new LinkedList<List<String>>();
	private final AttributeParsers attributeParsers;

	private final UiBinderContext uiBinderCtx;

	private final String binderUri;
	private final boolean isRenderer;

	public UiBinderWriter(JClassType baseClass, String implClassName, String templatePath, TypeOracle oracle, MortalLogger logger, FieldManager fieldManager,
			MessagesWriter messagesWriter, DesignTimeUtils designTime, UiBinderContext uiBinderCtx, boolean useSafeHtmlTemplates, boolean useLazyWidgetBuilders,
			String binderUri) throws UnableToCompleteException {
		this.baseClass = baseClass;
		this.implClassName = implClassName;
		this.oracle = oracle;
		this.logger = logger;
		this.templatePath = templatePath;
		this.fieldManager = fieldManager;
		this.messages = messagesWriter;
		this.designTime = designTime;
		this.uiBinderCtx = uiBinderCtx;
		this.useSafeHtmlTemplates = useSafeHtmlTemplates;
		this.useLazyWidgetBuilders = useLazyWidgetBuilders;
		this.binderUri = binderUri;

		this.htmlTemplates = new HtmlTemplatesWriter(fieldManager, logger);

		// Check for possible misuse 'GWT.create(UiBinder.class)'
		JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
		if (uibinderItself.equals(baseClass)) {
			die("You must use a subtype of UiBinder in GWT.create(). E.g.,\n" + "  interface Binder extends UiBinder<Widget, MyClass> {}\n"
					+ "  GWT.create(Binder.class);");
		}

		JClassType[] uiBinderTypes = baseClass.getImplementedInterfaces();
		if (uiBinderTypes.length == 0) {
			throw new RuntimeException("No implemented interfaces for " + baseClass.getName());
		}
		JClassType uiBinderType = uiBinderTypes[0];

		JClassType[] typeArgs = uiBinderType.isParameterized() == null ? new JClassType[0] : uiBinderType.isParameterized().getTypeArgs();

		String binderType = uiBinderType.getName();

		JClassType uiRendererClass = getOracle().findType(UiRenderer.class.getName());
		if (uiBinderType.isAssignableTo(uibinderItself)) {
			if (typeArgs.length < 2) {
				throw new RuntimeException("Root and owner type parameters are required for type %s" + binderType);
			}
			uiRootType = typeArgs[0];
			uiOwnerType = typeArgs[1];
			isRenderer = false;
		} else if (uiBinderType.isAssignableTo(uiRendererClass)) {
			if (typeArgs.length >= 1) {
				throw new RuntimeException("UiRenderer is not a parameterizable type in " + binderType);
			}
			if (!useSafeHtmlTemplates) {
				die("Configuration property UiBinder.useSafeHtmlTemplates\n" + "  must be set to true to generate a UiRenderer");
			}
			if (!useLazyWidgetBuilders) {
				die("Configuration property UiBinder.useLazyWidgetBuilders\n" + "  must be set to true to generate a UiRenderer");
			}

			// UiRenderers do not need owners but UiBinder generation needs some type
			// here
			uiOwnerType = uiBinderType;
			uiRootType = null;
			isRenderer = true;
		} else {
			die(baseClass.getName() + " must implement UiBinder or UiRenderer");
			// This is unreachable in practice, but silences not initialized errors
			throw new UnableToCompleteException();
		}

		isRenderableClassType = oracle.findType(IsRenderable.class.getCanonicalName());
		lazyDomElementClass = oracle.findType(LazyDomElement.class.getCanonicalName());

		ownerClass = new OwnerClass(uiOwnerType, logger, uiBinderCtx);
		bundleClass = new ImplicitClientBundle(baseClass.getPackage().getName(), this.implClassName, CLIENT_BUNDLE_FIELD, logger);
		handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle, useLazyWidgetBuilders);

		attributeParsers = new AttributeParsers(oracle, fieldManager, logger);
	}

	/**
	 * Add a statement to be executed right after the current attached element is
	 * detached. This is useful for doing things that might be expensive while the
	 * element is attached to the DOM.
	 * 
	 * @param format
	 * @param args
	 * @see #beginAttachedSection(String)
	 */
	public void addDetachStatement(String format, Object... args) {
		detachStatementsStack.getFirst().add(String.format(format, args));
	}

	/**
	 * Add a statement to be run after everything has been instantiated, in the
	 * style of {@link String#format}.
	 */
	public void addInitStatement(String format, Object... params) {
		initStatements.add(formatCode(format, params));
	}

	/**
	 * Adds a statement to the block run after fields are declared, in the style
	 * of {@link String#format}.
	 */
	public void addStatement(String format, Object... args) {
		String code = formatCode(format, args);

		if (useLazyWidgetBuilders) {
			/**
			 * I'm intentionally over-simplifying this and assuming that the input
			 * comes always in the format: field.somestatement(); Thus, field can be
			 * extracted easily and the element parsers don't need to be changed all
			 * at once.
			 */
			int idx = code.indexOf(".");
			String fieldName = code.substring(0, idx);
			fieldManager.require(fieldName).addStatement(format, args);
		} else {
			statements.add(code);
		}
	}

	/**
	 * Begin a section where a new attachable element is being parsed--that is,
	 * one that will be constructed as a big innerHTML string, and then briefly
	 * attached to the dom to allow fields accessing its to be filled (at the
	 * moment, HasHTMLParser, HTMLPanelParser, and DomElementParser.).
	 * <p>
	 * Succeeding calls made to {@link #ensureAttached} and
	 * {@link #ensureCurrentFieldAttached} must refer to children of this element,
	 * until {@link #endAttachedSection} is called.
	 * 
	 * @param element
	 *          Java expression for the generated code that will return the dom
	 *          element to be attached.
	 */
	public void beginAttachedSection(String element) {
		attachSectionElements.addFirst(element);
		detachStatementsStack.addFirst(new ArrayList<String>());
	}

	/**
	 * Declare a field that will hold an Element instance. Returns a token that
	 * the caller must set as the id attribute of that element in whatever
	 * innerHTML expression will reproduce it at runtime.
	 * <P>
	 * In the generated code, this token will be replaced by an expression to
	 * generate a unique dom id at runtime. Further code will be generated to be
	 * run after widgets are instantiated, to use that dom id in a getElementById
	 * call and assign the Element instance to its field.
	 * 
	 * @param fieldName
	 *          The name of the field being declared
	 * @param ancestorField
	 *          The name of fieldName parent
	 */
	public String declareDomField(XMLElement source, String fieldName, String ancestorField) throws UnableToCompleteException {
		ensureAttached();
		String name = declareDomIdHolder(fieldName);

		if (useLazyWidgetBuilders) {
			// Create and initialize the dom field with LazyDomElement.
			FieldWriter field = fieldManager.require(fieldName);

			/**
			 * But if the owner field is an instance of LazyDomElement then the code
			 * can be optimized, no cast is needed and the getter doesn't need to be
			 * called in its ancestral.
			 */
			if (isOwnerFieldLazyDomElement(fieldName)) {
				field.setInitializer(formatCode("new %s(%s)", field.getQualifiedSourceName(), fieldManager.convertFieldToGetter(name)));
			} else {

				field.setInitializer(formatCode("new %s(%s).get().cast()", LazyDomElement.class.getCanonicalName(), fieldManager.convertFieldToGetter(name)));

				// The dom must be created by its ancestor.
				fieldManager.require(ancestorField).addAttachStatement(fieldManager.convertFieldToGetter(fieldName) + ";");
			}
		} else {
			setFieldInitializer(fieldName, "null");
			addInitStatement("%s = com.google.gwt.dom.client.Document.get().getElementById(%s).cast();", fieldName, name);
			addInitStatement("%s.removeAttribute(\"id\");", fieldName);
		}

		return tokenForStringExpression(source, fieldManager.convertFieldToGetter(name));
	}

	/**
	 * Declare a variable that will be filled at runtime with a unique id, safe
	 * for use as a dom element's id attribute. For {@code UiRenderer} based code,
	 * elements corresponding to a ui:field, need and id initialized to a value
	 * that depends on the {@code fieldName}. For all other cases let
	 * {@code fieldName} be {@code null}.
	 * 
	 * @param fieldName
	 *          name of the field corresponding to this variable.
	 * @return that variable's name.
	 */
	public String declareDomIdHolder(String fieldName) throws UnableToCompleteException {
		String domHolderName = "domId" + domId++;
		FieldWriter domField = fieldManager.registerField(FieldWriterType.DOM_ID_HOLDER, oracle.findType(String.class.getName()), domHolderName);
		if (isRenderer && fieldName != null) {
			domField.setInitializer("buildInnerId(\"" + fieldName + "\", uiId)");
		} else {
			domField.setInitializer("com.google.gwt.dom.client.Document.get().createUniqueId()");
		}

		return domHolderName;
	}

	/**
	 * If this element has a gwt:field attribute, create a field for it of the
	 * appropriate type, and return the field name. If no gwt:field attribute is
	 * found, do nothing and return null
	 * 
	 * @return The new field name, or null if no field is created
	 */
	public String declareFieldIfNeeded(XMLElement elem) throws UnableToCompleteException {
		String fieldName = getFieldName(elem);
		if (fieldName != null) {

			/**
			 * We can switch types if useLazyWidgetBuilders is enabled and the
			 * respective owner field is a LazyDomElement.
			 */
			if (useLazyWidgetBuilders && isOwnerFieldLazyDomElement(fieldName)) {
				fieldManager.registerFieldForLazyDomElement(findFieldType(elem), ownerClass.getUiField(fieldName));
			} else {
				fieldManager.registerField(findFieldType(elem), fieldName);
			}
		}
		return fieldName;
	}

	/**
	 * Declare a {@link RenderableStamper} instance that will be filled at runtime
	 * with a unique token. This instance can then be used to stamp a single
	 * {@link IsRenderable}.
	 * 
	 * @return that variable's name.
	 */
	public String declareRenderableStamper() throws UnableToCompleteException {
		String renderableStamperName = "renderableStamper" + renderableStamper++;
		FieldWriter domField = fieldManager.registerField(FieldWriterType.RENDERABLE_STAMPER, oracle.findType(RenderableStamper.class.getName()),
				renderableStamperName);
		domField.setInitializer(formatCode("new %s(com.google.gwt.dom.client.Document.get().createUniqueId())", RenderableStamper.class.getName()));

		return renderableStamperName;
	}

	/**
	 * Writes a new SafeHtml template to the generated BinderImpl.
	 * 
	 * @return The invocation of the SafeHtml template function with the arguments
	 *         filled in
	 */
	public String declareTemplateCall(String html, String fieldName) throws IllegalArgumentException {
		if (!useSafeHtmlTemplates) {
			return '"' + html + '"';
		}
		FieldWriter w = fieldManager.lookup(fieldName);
		HtmlTemplateMethodWriter templateMethod = htmlTemplates.addSafeHtmlTemplate(html, tokenator);
		if (useLazyWidgetBuilders) {
			w.setHtml(templateMethod.getIndirectTemplateCall());
		} else {
			w.setHtml(templateMethod.getDirectTemplateCall());
		}
		return w.getHtml();
	}

	/**
	 * Given a string containing tokens returned by
	 * {@link #tokenForStringExpression}, {@link #tokenForSafeHtmlExpression} or
	 * {@link #declareDomField}, return a string with those tokens replaced by the
	 * appropriate expressions. (It is not normally necessary for an
	 * {@link XMLElement.Interpreter} or {@link ElementParser} to make this call,
	 * as the tokens are typically replaced by the TemplateWriter itself.)
	 */
	public String detokenate(String betokened) {
		return tokenator.detokenate(betokened);
	}

	/**
	 * Post an error message and halt processing. This method always throws an
	 * {@link UnableToCompleteException}
	 */
	public void die(String message) throws UnableToCompleteException {
		logger.die(message);
	}

	/**
	 * Post an error message and halt processing. This method always throws an
	 * {@link UnableToCompleteException}
	 */
	public void die(String message, Object... params) throws UnableToCompleteException {
		logger.die(message, params);
	}

	/**
	 * Post an error message about a specific XMLElement and halt processing. This
	 * method always throws an {@link UnableToCompleteException}
	 */
	public void die(XMLElement context, String message, Object... params) throws UnableToCompleteException {
		logger.die(context, message, params);
	}

	/**
	 * End the current attachable section. This will detach the element if it was
	 * ever attached and execute any detach statements.
	 * 
	 * @see #beginAttachedSection(String)
	 */
	public void endAttachedSection() {
		String elementVar = attachSectionElements.removeFirst();
		List<String> detachStatements = detachStatementsStack.removeFirst();
		if (attachedVars.containsKey(elementVar)) {
			String attachedVar = attachedVars.remove(elementVar);
			addInitStatement("%s.detach();", attachedVar);
			for (String statement : detachStatements) {
				addInitStatement(statement);
			}
		}
	}

	/**
	 * Ensure that the specified element is attached to the DOM.
	 * 
	 * @see #beginAttachedSection(String)
	 */
	public void ensureAttached() {
		String attachSectionElement = attachSectionElements.getFirst();
		if (!attachedVars.containsKey(attachSectionElement)) {
			String attachedVar = "attachRecord" + nextAttachVar;
			addInitStatement("UiBinderUtil.TempAttachment %s = UiBinderUtil.attachToDom(%s);", attachedVar, attachSectionElement);
			attachedVars.put(attachSectionElement, attachedVar);
			nextAttachVar++;
		}
	}

	/**
	 * Ensure that the specified field is attached to the DOM. The field must hold
	 * an object that responds to Element getElement(). Convenience wrapper for
	 * {@link #ensureAttached}<code>(field + ".getElement()")</code>.
	 * 
	 * @see #beginAttachedSection(String)
	 */
	public void ensureCurrentFieldAttached() {
		ensureAttached();
	}

	/**
	 * Finds the JClassType that corresponds to this XMLElement, which must be a
	 * Widget or an Element.
	 * 
	 * @throws UnableToCompleteException
	 *           If no such widget class exists
	 * @throws RuntimeException
	 *           if asked to handle a non-widget, non-DOM element
	 */
	public JClassType findFieldType(XMLElement elem) throws UnableToCompleteException {
		String tagName = elem.getLocalName();

		if (!isImportedElement(elem)) {
			return findDomElementTypeForTag(tagName);
		}

		String ns = elem.getNamespaceUri();
		String packageName = ns.substring(PACKAGE_URI_SCHEME.length());
		String className = tagName;

		while (true) {
			JClassType rtn = getOracle().findType(packageName + "." + className);
			if (rtn != null) {
				return rtn;
			}

			// Try again: shift one element of the class name onto the package name.
			// If the class name has only one element left, fail.
			int index = className.indexOf(".");
			if (index == -1) {
				die(elem, "No class matching \"%s\" in %s", tagName, ns);
			}
			packageName = packageName + "." + className.substring(0, index);
			className = className.substring(index + 1);
		}
	}

	/**
	 * Generates the code to set a property value (assumes that 'value' is a valid
	 * Java expression).
	 */
	public void genPropertySet(String fieldName, String propName, String value) {
		addStatement("%1$s.set%2$s(%3$s);", fieldName, capitalizePropName(propName), value);
	}

	/**
	 * Generates the code to set a string property.
	 */
	public void genStringPropertySet(String fieldName, String propName, String value) {
		genPropertySet(fieldName, propName, "\"" + value + "\"");
	}

	/**
	 * The type we have been asked to generated, e.g. MyUiBinder
	 */
	public JClassType getBaseClass() {
		return baseClass;
	}

	public ImplicitClientBundle getBundleClass() {
		return bundleClass;
	}

	/**
	 * Returns the {@link DesignTimeUtils}, not <code>null</code>.
	 */
	public DesignTimeUtils getDesignTime() {
		return designTime;
	}

	public FieldManager getFieldManager() {
		return fieldManager;
	}

	/**
	 * Returns the logger, at least until we get get it handed off to parsers via
	 * constructor args.
	 */
	public MortalLogger getLogger() {
		return logger;
	}

	/**
	 * Get the {@link MessagesWriter} for this UI, generating it if necessary.
	 */
	public MessagesWriter getMessages() {
		return messages;
	}

	/**
	 * Gets the type oracle.
	 */
	public TypeOracle getOracle() {
		return oracle;
	}

	public OwnerClass getOwnerClass() {
		return ownerClass;
	}

	public String getUiFieldAttributeName() {
		return gwtPrefix + ":field";
	}

	public boolean isBinderElement(XMLElement elem) {
		String uri = elem.getNamespaceUri();
		return uri != null && binderUri.equals(uri);
	}

	public boolean isElementAssignableTo(XMLElement elem, Class<?> possibleSuperclass) throws UnableToCompleteException {
		JClassType classType = oracle.findType(possibleSuperclass.getCanonicalName());
		return isElementAssignableTo(elem, classType);
	}

	public boolean isElementAssignableTo(XMLElement elem, JClassType possibleSupertype) throws UnableToCompleteException {
		/*
		 * Things like <W extends IsWidget & IsPlaid>
		 */
		JTypeParameter typeParameter = possibleSupertype.isTypeParameter();
		if (typeParameter != null) {
			JClassType[] bounds = typeParameter.getBounds();
			for (JClassType bound : bounds) {
				if (!isElementAssignableTo(elem, bound)) {
					return false;
				}
			}
			return true;
		}

		/*
		 * Binder fields are always declared raw, so we're cheating if the user is
		 * playing with parameterized types. We're happy enough if the raw types
		 * match, and rely on them to make sure the specific types really do work.
		 */
		JParameterizedType parameterized = possibleSupertype.isParameterized();
		if (parameterized != null) {
			return isElementAssignableTo(elem, parameterized.getRawType());
		}

		JClassType fieldtype = findFieldType(elem);
		if (fieldtype == null) {
			return false;
		}
		return fieldtype.isAssignableTo(possibleSupertype);
	}

	public boolean isImportedElement(XMLElement elem) {
		String uri = elem.getNamespaceUri();
		return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);
	}

	/**
	 * Checks whether the given owner field name is a LazyDomElement or not.
	 */
	public boolean isOwnerFieldLazyDomElement(String fieldName) {
		OwnerField ownerField = ownerClass.getUiField(fieldName);
		if (ownerField == null) {
			return false;
		}

		return lazyDomElementClass.isAssignableFrom(ownerField.getType().getRawType());
	}

	public boolean isRenderableElement(XMLElement elem) throws UnableToCompleteException {
		return findFieldType(elem).isAssignableTo(isRenderableClassType);
	}

	public boolean isRenderer() {
		return isRenderer;
	}

	public boolean isWidgetElement(XMLElement elem) throws UnableToCompleteException {
		return isElementAssignableTo(elem, IsWidget.class);
	}

	/**
	 * Parses the object associated with the specified element, and returns the
	 * field writer that will hold it. The element is likely to make recursive
	 * calls back to this method to have its children parsed.
	 * 
	 * @param elem
	 *          the xml element to be parsed
	 * @return the field holder just created
	 */
	public FieldWriter parseElementToField(XMLElement elem) throws UnableToCompleteException {
		if (elementParsers.isEmpty()) {
			registerParsers();
		}

		// Get the class associated with this element.
		JClassType type = findFieldType(elem);

		// Declare its field.
		FieldWriter field = declareField(elem, type.getQualifiedSourceName());

		/*
		 * Push the field that will hold this widget on top of the parsedFieldStack
		 * to ensure that fields registered by its parsers will be noted as
		 * dependencies of the new widget. (See registerField.) Also push the
		 * element being parsed, so that the fieldManager can hold that info for
		 * later error reporting when field reference left hand sides are validated.
		 */
		fieldManager.push(elem, field);

		// Give all the parsers a chance to generate their code.
		for (ElementParser parser : getParsersForClass(type)) {
			parser.parse(elem, field.getName(), type, this);
		}
		fieldManager.pop();

		return field;
	}

	/**
	 * Gives the writer the initializer to use for this field instead of the
	 * default GWT.create call.
	 * 
	 * @throws IllegalStateException
	 *           if an initializer has already been set
	 */
	public void setFieldInitializer(String fieldName, String factoryMethod) {
		fieldManager.lookup(fieldName).setInitializer(factoryMethod);
	}

	/**
	 * Instructs the writer to initialize the field with a specific constructor
	 * invocation, instead of the default GWT.create call.
	 * 
	 * @param fieldName
	 *          the field to initialize
	 * @param args
	 *          arguments to the constructor call
	 */
	public void setFieldInitializerAsConstructor(String fieldName, String... args) {
		JClassType assignableType = fieldManager.lookup(fieldName).getAssignableType();
		setFieldInitializer(fieldName, formatCode("new %s(%s)", assignableType.getQualifiedSourceName(), asCommaSeparatedList(args)));
	}

	/**
	 * Like {@link #tokenForStringExpression}, but used for runtime expressions
	 * that we trust to be safe to interpret at runtime as HTML without escaping,
	 * like translated messages with simple formatting. Wrapped in a call to
	 * {@link com.google.gwt.safehtml.shared.SafeHtmlUtils#fromSafeConstant} to
	 * keep the expression from being escaped by the SafeHtml template.
	 * 
	 * @param expression
	 *          must resolve to trusted HTML string
	 */
	public String tokenForSafeConstant(XMLElement source, String expression) {
		if (!useSafeHtmlTemplates) {
			return tokenForStringExpression(source, expression);
		}

		expression = "SafeHtmlUtils.fromSafeConstant(" + expression + ")";
		htmlTemplates.noteSafeConstant(expression);
		return nextToken(source, expression);
	}

	/**
	 * Like {@link #tokenForStringExpression}, but used for runtime
	 * {@link com.google.gwt.safehtml.shared.SafeHtml SafeHtml} instances.
	 * 
	 * @param expression
	 *          must resolve to SafeHtml object
	 */
	public String tokenForSafeHtmlExpression(XMLElement source, String expression) {
		if (!useSafeHtmlTemplates) {
			return tokenForStringExpression(source, expression + ".asString()");
		}

		htmlTemplates.noteSafeConstant(expression);
		return nextToken(source, expression);
	}

	/**
	 * Like {@link #tokenForStringExpression}, but used for runtime
	 * {@link com.google.gwt.safehtml.shared.SafeUri SafeUri} instances.
	 * 
	 * @param expression
	 *          must resolve to SafeUri object
	 */
	public String tokenForSafeUriExpression(XMLElement source, String expression) {
		if (!useSafeHtmlTemplates) {
			return tokenForStringExpression(source, expression);
		}

		htmlTemplates.noteUri(expression);
		return nextToken(source, expression);
	}

	/**
	 * Returns a string token that can be used in place the given expression
	 * inside any string literals. Before the generated code is written, the
	 * expression will be stitched back into the generated code in place of the
	 * token, surrounded by plus signs. This is useful in strings to be handed to
	 * setInnerHTML() and setText() calls, to allow a unique dom id attribute or
	 * other runtime expression in the string.
	 * 
	 * @param expression
	 *          must resolve to String
	 */
	public String tokenForStringExpression(XMLElement source, String expression) {
		return nextToken(source, "\" + " + expression + " + \"");
	}

	public boolean useLazyWidgetBuilders() {
		return useLazyWidgetBuilders;
	}

	/**
	 * @return true of SafeHtml integration is in effect
	 */
	public boolean useSafeHtmlTemplates() {
		return useSafeHtmlTemplates;
	}

	/**
	 * Post a warning message.
	 */
	public void warn(String message) {
		logger.warn(message);
	}

	/**
	 * Post a warning message.
	 */
	public void warn(String message, Object... params) {
		logger.warn(message, params);
	}

	/**
	 * Post a warning message.
	 */
	public void warn(XMLElement context, String message, Object... params) {
		logger.warn(context, message, params);
	}

	/**
	 * Entry point for the code generation logic. It generates the
	 * implementation's superstructure, and parses the root widget (leading to all
	 * of its children being parsed as well).
	 * 
	 * @param doc
	 *          TODO
	 */
	protected void parseDocument(Document doc, PrintWriter printWriter) throws UnableToCompleteException {
		Element documentElement = doc.getDocumentElement();
		gwtPrefix = documentElement.lookupPrefix(binderUri);

		XMLElement elem = new XMLElementProviderImpl(attributeParsers, oracle, logger, designTime).get(documentElement);
		this.rendered = tokenator.detokenate(parseDocumentElement(elem));
		printWriter.print(rendered);
	}

	private void addElementParser(String gwtClass, String parser) {
		elementParsers.put(gwtClass, parser);
	}

	private void addWidgetParser(String className) {
		String gwtClass = "com.google.gwt.user.client.ui." + className;
		String parser = "fr.onevu.gwt.uibinder.elementparsers." + className + "Parser";
		addElementParser(gwtClass, parser);
	}

	/**
	 * Declares a field of the given type name, returning the name of the declared
	 * field. If the element has a field or id attribute, use its value.
	 * Otherwise, create and return a new, private field name for it.
	 */
	private FieldWriter declareField(XMLElement source, String typeName) throws UnableToCompleteException {
		JClassType type = oracle.findType(typeName);
		if (type == null) {
			die(source, "Unknown type %s", typeName);
		}

		String fieldName = getFieldName(source);
		if (fieldName == null) {
			// TODO(rjrjr) could collide with user declared name, as is
			// also a worry in HandlerEvaluator. Need a general scheme for
			// anonymous fields. See the note in HandlerEvaluator and do
			// something like that, but in FieldManager.
			fieldName = "f_" + source.getLocalName() + ++fieldIndex;
		}
		fieldName = normalizeFieldName(fieldName);
		return fieldManager.registerField(type, fieldName);
	}

	private void dieGettingEventTypeName(JMethod jMethod, Exception e) throws UnableToCompleteException {
		die("Could not obtain DomEvent.Type object for first parameter of %s (%s)", formatMethodError(jMethod), e.getMessage());
	}

	/**
	 * Ensures that all of the internal data structures are cleaned up correctly
	 * at the end of parsing the document.
	 * 
	 * @throws IllegalStateException
	 */
	private void ensureAttachmentCleanedUp() {
		if (!attachSectionElements.isEmpty()) {
			throw new IllegalStateException("Attachments not cleaned up: " + attachSectionElements);
		}
		if (!detachStatementsStack.isEmpty()) {
			throw new IllegalStateException("Detach not cleaned up: " + detachStatementsStack);
		}
	}

	/**
	 * Add call to
	 * {@code com.google.gwt.resources.client.CssResource#ensureInjected()} on
	 * each CSS resource field.
	 */
	private void ensureInjectedCssFields() {
		for (ImplicitCssResource css : bundleClass.getCssMethods()) {
			String fieldName = css.getName();
			FieldWriter cssField = fieldManager.require(fieldName);
			cssField.addStatement("%s.ensureInjected();", fieldName);
		}
	}

	/**
	 * Evaluate whether all @UiField attributes are also defined in the template.
	 * Dies if not.
	 */
	private void evaluateUiFields() throws UnableToCompleteException {
		if (designTime.isDesignTime()) {
			return;
		}
		for (OwnerField ownerField : getOwnerClass().getUiFields()) {
			String fieldName = ownerField.getName();
			FieldWriter fieldWriter = fieldManager.lookup(fieldName);

			if (fieldWriter == null) {
				die("Template %s has no %s attribute for %s.%s#%s", templatePath, getUiFieldAttributeName(), uiOwnerType.getPackage().getName(), uiOwnerType.getName(),
						fieldName);
			}
		}
	}

	/**
	 * Given a DOM tag name, return the corresponding JSO subclass.
	 */
	private JClassType findDomElementTypeForTag(String tag) {
		JClassType elementClass = oracle.findType("com.google.gwt.dom.client.Element");
		JClassType[] types = elementClass.getSubtypes();
		for (JClassType type : types) {
			TagName annotation = type.getAnnotation(TagName.class);
			if (annotation != null) {
				for (String annotationTag : annotation.value()) {
					if (annotationTag.equals(tag)) {
						return type;
					}
				}
			}
		}
		return elementClass;
	}

	/**
	 * Calls {@code getType().getName()} on subclasses of {@code DomEvent}.
	 */
	private String findEventTypeName(JMethod jMethod) throws UnableToCompleteException {
		// Get the event class name (i.e. ClickEvent)
		String eventTypeName = jMethod.getParameterTypes()[0].getQualifiedSourceName();

		Class<?> domType;

		// Get the class instance
		try {
			domType = Class.forName(eventTypeName);
		} catch (ClassNotFoundException e) {
			die("Could not find type %s in %s", eventTypeName, formatMethodError(jMethod));
			return null;
		}

		// Reflectively obtain the type (i.e. ClickEvent.getType())
		try {
			return ((Type<?>) domType.getMethod("getType", (Class[]) null).invoke(null, (Object[]) null)).getName();
		} catch (IllegalArgumentException e) {
			dieGettingEventTypeName(jMethod, e);
		} catch (SecurityException e) {
			dieGettingEventTypeName(jMethod, e);
		} catch (IllegalAccessException e) {
			dieGettingEventTypeName(jMethod, e);
		} catch (InvocationTargetException e) {
			dieGettingEventTypeName(jMethod, e);
		} catch (NoSuchMethodException e) {
			dieGettingEventTypeName(jMethod, e);
		}
		// Unreachable, but appeases the compiler
		return null;
	}

	/**
	 * Use this method to format code. It forces the use of the en-US locale, so
	 * that things like decimal format don't get mangled.
	 */
	private String formatCode(String format, Object... params) {
		String r = String.format(Locale.US, format, params);
		return r;
	}

	/**
	 * Inspects this element for a gwt:field attribute. If one is found, the
	 * attribute is consumed and its value returned.
	 * 
	 * @return The field name declared by an element, or null if none is declared
	 */
	private String getFieldName(XMLElement elem) throws UnableToCompleteException {
		String fieldName = null;
		boolean hasOldSchoolId = false;
		if (elem.hasAttribute("id") && isWidgetElement(elem)) {
			hasOldSchoolId = true;
			// If an id is specified on the element, use that.
			fieldName = elem.consumeRawAttribute("id");
			warn(elem, "Deprecated use of id=\"%1$s\" for field name. " + "Please switch to gwt:field=\"%1$s\" instead. " + "This will soon be a compile error!",
					fieldName);
		}
		if (elem.hasAttribute(getUiFieldAttributeName())) {
			if (hasOldSchoolId) {
				die(elem, "Cannot declare both id and field on the same element");
			}
			fieldName = elem.consumeRawAttribute(getUiFieldAttributeName());
		}
		return fieldName;
	}

	private Class<? extends ElementParser> getParserForClass(JClassType uiClass) {
		// Find the associated parser.
		String uiClassName = uiClass.getQualifiedSourceName();
		String parserClassName = elementParsers.get(uiClassName);
		if (parserClassName == null) {
			return null;
		}

		// And instantiate it.
		try {
			return Class.forName(parserClassName).asSubclass(ElementParser.class);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("Unable to instantiate parser", e);
		} catch (ClassCastException e) {
			throw new RuntimeException(parserClassName + " must extend ElementParser");
		}
	}

	/**
	 * Find a set of element parsers for the given ui type.
	 * 
	 * The list of parsers will be returned in order from most- to least-specific.
	 */
	private Iterable<ElementParser> getParsersForClass(JClassType type) {
		List<ElementParser> parsers = new ArrayList<ElementParser>();

		/*
		 * Let this non-widget parser go first (it finds <m:attribute/> elements).
		 * Any other such should land here too.
		 * 
		 * TODO(rjrjr) Need a scheme to associate these with a namespace uri or
		 * something?
		 */
		parsers.add(new AttributeMessageParser());
		parsers.add(new UiChildParser(uiBinderCtx));

		for (JClassType curType : getClassHierarchyBreadthFirst(type)) {
			try {
				Class<? extends ElementParser> cls = getParserForClass(curType);
				if (cls != null) {
					ElementParser parser = cls.newInstance();
					parsers.add(parser);
				}
			} catch (InstantiationException e) {
				throw new RuntimeException("Unable to instantiate " + curType.getName(), e);
			} catch (IllegalAccessException e) {
				throw new RuntimeException("Unable to instantiate " + curType.getName(), e);
			}
		}

		parsers.add(new BeanParser(uiBinderCtx));
		parsers.add(new IsEmptyParser());

		return parsers;
	}

	/**
	 * Writes a field setter if the field is not provided and the field class is
	 * compatible with its respective template field.
	 */
	private void maybeWriteFieldSetter(IndentedWriter niceWriter, OwnerField ownerField, JClassType templateClass, String templateField)
			throws UnableToCompleteException {
		JClassType fieldType = ownerField.getType().getRawType();

		if (!ownerField.isProvided()) {
			/*
			 * Normally check that the type the template created can be slammed into
			 * the @UiField annotated field in the owning class
			 */
			if (!templateClass.isAssignableTo(fieldType)) {
				die("In @UiField %s, template field and owner field types don't match: %s is not assignable to %s", ownerField.getName(),
						templateClass.getQualifiedSourceName(), fieldType.getQualifiedSourceName());
			}
			/*
			 * And initialize the field
			 */
			niceWriter.write("owner.%1$s = %2$s;", ownerField.getName(), templateField);
		} else {
			/*
			 * But with @UiField(provided=true) the user builds it, so reverse the
			 * direction of the assignability check and do no init.
			 */
			if (!fieldType.isAssignableTo(templateClass)) {
				die("In UiField(provided = true) %s, template field and field types don't match: " + "@UiField(provided=true)%s is not assignable to %s",
						ownerField.getName(), fieldType.getQualifiedSourceName(), templateClass.getQualifiedSourceName());
			}
		}
	}

	private String nextToken(XMLElement source, String expression) {
		String nextToken = tokenator.nextToken(source, expression);
		return nextToken;
	}

	private String normalizeFieldName(String fieldName) {
		// If a field name has a '.' in it, replace it with '$' to make it a legal
		// identifier. This can happen with the field names associated with nested
		// classes.
		return fieldName.replace('.', '$');
	}

	/**
	 * Parse the document element and return the source of the Java class that
	 * will implement its UiBinder.
	 */
	private String parseDocumentElement(XMLElement elem) throws UnableToCompleteException {
		fieldManager.registerFieldOfGeneratedType(oracle.findType(ClientBundle.class.getName()), bundleClass.getPackageName(), bundleClass.getClassName(),
				bundleClass.getFieldName());

		// Allow GWT.create() to init the field, the default behavior

		FieldWriter rootField = new UiBinderParser(this, messages, fieldManager, oracle, bundleClass, binderUri, uiBinderCtx).parse(elem);

		fieldManager.validate();

		StringWriter stringWriter = new StringWriter();
		IndentedWriter niceWriter = new IndentedWriter(new PrintWriter(stringWriter));

		if (isRenderer) {
			ensureInjectedCssFields();
			writeRenderer(niceWriter, rootField);
		} else if (useLazyWidgetBuilders) {
			ensureInjectedCssFields();
			writeBinderForRenderableStrategy(niceWriter, rootField);
		} else {
			writeBinder(niceWriter, rootField);
		}
		ensureAttachmentCleanedUp();
		return stringWriter.toString();
	}

	private void registerParsers() {
		// TODO(rjrjr): Allow third-party parsers to register themselves
		// automagically

		addElementParser("com.google.gwt.dom.client.Element", "fr.onevu.gwt.uibinder.elementparsers.DomElementParser");

		// Register widget parsers.
		addWidgetParser("UIObject");
		addWidgetParser("HasText");
		addWidgetParser("HasHTML");
		addWidgetParser("HasTreeItems");
		addWidgetParser("HasWidgets");
		addWidgetParser("HTMLPanel");
		addWidgetParser("FlowPanel");
		addWidgetParser("AbsolutePanel");
		addWidgetParser("DockPanel");
		addWidgetParser("StackPanel");
		addWidgetParser("DisclosurePanel");
		addWidgetParser("TabPanel");
		addWidgetParser("MenuItem");
		addWidgetParser("MenuBar");
		addWidgetParser("CellPanel");
		addWidgetParser("CustomButton");
		addWidgetParser("DialogBox");
		addWidgetParser("LayoutPanel");
		addWidgetParser("DockLayoutPanel");
		addWidgetParser("StackLayoutPanel");
		addWidgetParser("TabLayoutPanel");
		addWidgetParser("Image");
		addWidgetParser("ListBox");
		addWidgetParser("Grid");
		addWidgetParser("HasAlignment");
		addWidgetParser("DateLabel");
		addWidgetParser("NumberLabel");
		if (useLazyWidgetBuilders) {
			addWidgetParser("LazyPanel");
			addWidgetParser("RenderablePanel");
		}
	}

	/**
	 * Validates each {@code eventMethod} (e.g.
	 * {@code onBrowserEvent(HandlerType o, NativeEvent e,
	 * Element parent, A a, B b, ...)}).
	 * <ul>
	 * <li>The second parameter type is {@code NativeEvent}
	 * <li>The third parameter type is {@code Element}
	 * <li>All the handler methods in the type of the first parameter (any methods
	 * annotated with {@code @UiHandler}) have a signature compatible with the
	 * {@code eventMethod}
	 * </ul>
	 */
	private void validateEventMethod(JMethod eventMethod) throws UnableToCompleteException {
		JParameter[] parameters = eventMethod.getParameters();
		if (parameters.length < 3) {
			die("Too few parameters in %s", formatMethodError(eventMethod));
		}

		String nativeEventName = NativeEvent.class.getCanonicalName();
		JClassType nativeEventType = oracle.findType(nativeEventName);
		if (!nativeEventType.equals(parameters[1].getType())) {
			die("Second parameter must be of type %s in %s", nativeEventName, formatMethodError(eventMethod));
		}

		String elementName = com.google.gwt.dom.client.Element.class.getCanonicalName();
		JClassType elementType = oracle.findType(elementName);
		if (!elementType.equals(parameters[2].getType())) {
			die("Third parameter must be of type %s in %s", elementName, formatMethodError(eventMethod));
		}

		if (parameters[0].getType().isClassOrInterface() == null) {
			die("First parameter must be a class or interface in %s", formatMethodError(eventMethod));
		}

		JClassType eventReceiver = parameters[0].getType().isClassOrInterface();

		validateEventReceiver(parameters, eventReceiver, eventMethod);
	}

	/**
	 * Validates the signature of all methods annotated with {@code @UiHandler} in
	 * the {@code eventReceiver} type. All event handlers must have the same
	 * signature where:
	 * <ul>
	 * <li>The annotation must list valid {@code ui:field}s
	 * <li>The first parameter must be assignable to
	 * {@link com.google.gwt.event.dom.client.DomEvent DomEvent}
	 * <li>If present, the second parameter must be of type
	 * {@link com.google.gwt.dom.client.Element Element}
	 * <li>For all other parameters in position {@code n} must be of the same type
	 * as {@code parameters[n + 1]}
	 * </ul>
	 */
	private void validateEventReceiver(JParameter[] onBrowserEventParameters, JClassType eventReceiver, JMethod sourceMethod) throws UnableToCompleteException {

		// Pre-compute the expected parameter types (after the first one, that is)
		JType[] onBrowserEventParamTypes = new JType[onBrowserEventParameters.length - 2];

		// If present, second parameter must be an Element
		onBrowserEventParamTypes[0] = oracle.findType(com.google.gwt.dom.client.Element.class.getCanonicalName());
		// And the rest must be the same type
		for (int i = 3; i < onBrowserEventParameters.length; i++) {
			onBrowserEventParamTypes[i - 2] = onBrowserEventParameters[i].getType();
		}

		for (JMethod jMethod : eventReceiver.getInheritableMethods()) {
			Class<UiHandler> annotationClass = UiHandler.class;
			UiHandler annotation = jMethod.getAnnotation(annotationClass);
			// Ignore methods not annotated with @UiHandler
			if (annotation == null) {
				continue;
			}
			// Are the fields in @UiHandler known?
			String[] fields = annotation.value();
			if (fields == null) {
				die("@UiHandler returns null from its value in %s", formatMethodError(jMethod));
			}
			for (String fieldName : fields) {
				FieldWriter field = fieldManager.lookup(fieldName);
				if (field == null) {
					die("\"%s\" is not a known field name as listed in the @UiHandler annotation in %s", fieldName, formatMethodError(jMethod));
				}
			}

			// First parameter
			JParameter[] eventHandlerParameters = jMethod.getParameters();
			JClassType domEventType = oracle.findType(DomEvent.class.getCanonicalName());
			JClassType firstParamType = eventHandlerParameters[0].getType().isClassOrInterface();
			if (firstParamType == null || !firstParamType.isAssignableTo(domEventType)) {
				die("First parameter must be assignable to com.google.gwt.dom.client.DomEvent in %s", formatMethodError(jMethod));
			}

			// All others
			if (onBrowserEventParamTypes.length < eventHandlerParameters.length - 1) {
				die("Too many parameters in %s", formatMethodError(jMethod));
			}
			for (int i = 1; i < eventHandlerParameters.length; i++) {
				if (!eventHandlerParameters[i].getType().equals(onBrowserEventParamTypes[i - 1])) {
					die("Parameter %s in %s is not of the same type as parameter %s in %s", eventHandlerParameters[i].getName(), formatMethodError(jMethod),
							onBrowserEventParameters[i + 1].getName(), formatMethodError(sourceMethod));
				}
			}
		}
	}

	/**
	 * Scan the base class for the getter methods. Assumes getters begin with
	 * "get" and validates that each corresponds to a field declared with
	 * {@code ui:field}. If the getter return type is assignable to
	 * {@code Element}, the getter must have a single parameter and the parameter
	 * must be assignable to {@code Element}. If the getter return type is
	 * assignable to {@code com.google.gwt.resources.client.CssResource}, the
	 * getter must have no parameters.
	 */
	private void validateRendererGetters(JClassType owner) throws UnableToCompleteException {
		for (JMethod jMethod : owner.getInheritableMethods()) {
			String getterName = jMethod.getName();
			if (getterName.startsWith("get")) {
				String fieldName = getterToFieldName(getterName);
				FieldWriter field = fieldManager.lookup(fieldName);
				if (field == null || (!FieldWriterType.DEFAULT.equals(field.getFieldType()) && !FieldWriterType.GENERATED_CSS.equals(field.getFieldType()))) {
					die("%s does not match a \"ui:field='%s'\" declaration in %s, " + "or '%s' refers to something other than a ui:style"
							+ " or an HTML element in the template", getterName, fieldName, owner.getQualifiedSourceName(), fieldName);
				}
				if (FieldWriterType.DEFAULT.equals(field.getFieldType()) && jMethod.getParameterTypes().length != 1) {
					die("Field getter %s must have exactly one parameter in %s", getterName, owner.getQualifiedSourceName());
				} else if (FieldWriterType.GENERATED_CSS.equals(field.getFieldType()) && jMethod.getParameterTypes().length != 0) {
					die("Style getter %s must have no parameters in %s", getterName, owner.getQualifiedSourceName());
				} else if (jMethod.getParameterTypes().length == 1) {
					String elementClassName = com.google.gwt.dom.client.Element.class.getCanonicalName();
					JClassType elementType = oracle.findType(elementClassName);
					JClassType getterParamType = jMethod.getParameterTypes()[0].getErasedType().isClassOrInterface();

					if (!elementType.isAssignableFrom(getterParamType)) {
						die("Getter %s must have exactly one parameter of type assignable to %s in %s", getterName, elementClassName, owner.getQualifiedSourceName());
					}
				}
			} else if (!getterName.equals("render") && !getterName.equals("onBrowserEvent") && !getterName.equals("isParentOrRenderer")) {
				die("Unexpected method \"%s\" found in %s", getterName, owner.getQualifiedSourceName());
			}
		}
	}

	/**
	 * Scans a class to validate that it contains a single method called render,
	 * which has a {@code void} return type, and its first parameter is of type
	 * {@code SafeHtmlBuilder}.
	 */
	private void validateRenderParameters(JClassType owner) throws UnableToCompleteException {
		JMethod[] methods = owner.getInheritableMethods();
		JMethod renderMethod = null;

		for (JMethod jMethod : methods) {
			if (jMethod.getName().equals("render")) {
				if (renderMethod == null) {
					renderMethod = jMethod;
				} else {
					die("%s declares more than one method named render", owner.getQualifiedSourceName());
				}
			}
		}

		if (renderMethod == null || renderMethod.getParameterTypes().length < 1
				|| !renderMethod.getParameterTypes()[0].getErasedType().getQualifiedSourceName().equals(SafeHtmlBuilder.class.getCanonicalName())) {
			die("%s does not declare a render(SafeHtmlBuilder ...) method", owner.getQualifiedSourceName());
		}
		if (!JPrimitiveType.VOID.equals(renderMethod.getReturnType())) {
			die("%s#render(SafeHtmlBuilder ...) does not return void", owner.getQualifiedSourceName());
		}
	}

	/**
	 * Write statements that parsers created via calls to {@link #addStatement}.
	 * Such statements will assume that {@link #writeGwtFields} has already been
	 * called.
	 */
	private void writeAddedStatements(IndentedWriter niceWriter) {
		for (String s : statements) {
			niceWriter.write(s);
		}
	}

	/**
	 * Writes the UiBinder's source.
	 */
	private void writeBinder(IndentedWriter w, FieldWriter rootField) throws UnableToCompleteException {
		writePackage(w);

		writeImports(w);
		w.newline();

		writeClassOpen(w);
		writeStatics(w);
		w.newline();

		// Create SafeHtml Template
		writeTemplatesInterface(w);
		w.newline();

		// createAndBindUi method
		w.write("public %s createAndBindUi(final %s owner) {", uiRootType.getParameterizedQualifiedSourceName(), uiOwnerType.getParameterizedQualifiedSourceName());
		w.indent();
		w.newline();

		writeGwtFields(w);
		w.newline();

		designTime.writeAttributes(this);
		writeAddedStatements(w);
		w.newline();

		writeInitStatements(w);
		w.newline();

		writeHandlers(w);
		w.newline();

		writeOwnerFieldSetters(w);

		writeCssInjectors(w);

		w.write("return %s;", rootField.getNextReference());
		w.outdent();
		w.write("}");

		// Close class
		w.outdent();
		w.write("}");
	}

	/**
	 * Writes a different optimized UiBinder's source for the renderable strategy.
	 */
	private void writeBinderForRenderableStrategy(IndentedWriter w, FieldWriter rootField) throws UnableToCompleteException {
		writePackage(w);

		writeImports(w);
		w.newline();

		writeClassOpen(w);
		writeStatics(w);
		w.newline();

		writeTemplatesInterface(w);

		w.newline();

		// createAndBindUi method
		w.write("public %s createAndBindUi(final %s owner) {", uiRootType.getParameterizedQualifiedSourceName(), uiOwnerType.getParameterizedQualifiedSourceName());
		w.indent();
		w.newline();

		designTime.writeAttributes(this);
		w.newline();

		w.write("return new Widgets(owner).%s;", rootField.getNextReference());
		w.outdent();
		w.write("}");

		// Writes the inner class Widgets.
		w.newline();
		w.write("/**");
		w.write(" * Encapsulates the access to all inner widgets");
		w.write(" */");
		w.write("class Widgets {");
		w.indent();

		String ownerClassType = uiOwnerType.getParameterizedQualifiedSourceName();
		w.write("private final %s owner;", ownerClassType);
		w.newline();

		writeHandlers(w);
		w.newline();

		w.write("public Widgets(final %s owner) {", ownerClassType);
		w.indent();
		w.write("this.owner = owner;");
		fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
		w.outdent();
		w.write("}");
		w.newline();

		htmlTemplates.writeTemplateCallers(w);

		evaluateUiFields();

		fieldManager.writeFieldDefinitions(w, getOracle(), getOwnerClass(), getDesignTime());

		w.outdent();
		w.write("}");

		// Close class
		w.outdent();
		w.write("}");
	}

	private void writeClassOpen(IndentedWriter w) {
		if (!isRenderer) {
			w.write("public class %s implements UiBinder<%s, %s>, %s {", implClassName, uiRootType.getParameterizedQualifiedSourceName(),
					uiOwnerType.getParameterizedQualifiedSourceName(), baseClass.getParameterizedQualifiedSourceName());
		} else {
			w.write("public class %s extends %s implements %s {", implClassName, AbstractUiRenderer.class.getName(), baseClass.getParameterizedQualifiedSourceName());
		}
		w.indent();
	}

	private void writeCssInjectors(IndentedWriter w) {
		for (ImplicitCssResource css : bundleClass.getCssMethods()) {
			w.write("%s.%s().ensureInjected();", bundleClass.getFieldName(), css.getName());
		}
		w.newline();
	}

	/**
	 * Write declarations for variables or fields to hold elements declared with
	 * gwt:field in the template. For those that have not had constructor
	 * generation suppressed, emit GWT.create() calls instantiating them (or die
	 * if they have no default constructor).
	 * 
	 * @throws UnableToCompleteException
	 *           on constructor problem
	 */
	private void writeGwtFields(IndentedWriter niceWriter) throws UnableToCompleteException {
		// For each provided field in the owner class, initialize from the owner
		Collection<OwnerField> ownerFields = getOwnerClass().getUiFields();
		for (OwnerField ownerField : ownerFields) {
			if (ownerField.isProvided()) {
				String fieldName = ownerField.getName();
				FieldWriter fieldWriter = fieldManager.lookup(fieldName);

				// TODO why can this be null?
				if (fieldWriter != null) {
					String initializer;
					if (designTime.isDesignTime()) {
						String typeName = ownerField.getType().getRawType().getQualifiedSourceName();
						initializer = designTime.getProvidedField(typeName, ownerField.getName());
					} else {
						initializer = formatCode("owner.%1$s", fieldName);
					}
					fieldManager.lookup(fieldName).setInitializer(initializer);
				}
			}
		}

		fieldManager.writeGwtFieldsDeclaration(niceWriter);
	}

	private void writeHandlers(IndentedWriter w) throws UnableToCompleteException {
		if (designTime.isDesignTime()) {
			return;
		}
		handlerEvaluator.run(w, fieldManager, "owner");
	}

	private void writeImports(IndentedWriter w) {
		w.write("import com.google.gwt.core.client.GWT;");
		w.write("import com.google.gwt.dom.client.Element;");
		if (!(htmlTemplates.isEmpty())) {
			w.write("import com.google.gwt.safehtml.client.SafeHtmlTemplates;");
			w.write("import com.google.gwt.safehtml.shared.SafeHtml;");
			w.write("import com.google.gwt.safehtml.shared.SafeHtmlUtils;");
			w.write("import com.google.gwt.safehtml.shared.SafeHtmlBuilder;");
			w.write("import com.google.gwt.safehtml.shared.SafeUri;");
			w.write("import com.google.gwt.safehtml.shared.UriUtils;");
			w.write("import fr.onevu.gwt.uibinder.client.UiBinderUtil;");
		}

		if (!isRenderer) {
			w.write("import com.google.gwt.uibinder.client.UiBinder;");
			w.write("import fr.onevu.gwt.uibinder.client.UiBinderUtil;");
			w.write("import %s.%s;", uiRootType.getPackage().getName(), uiRootType.getName());
		} else {
			w.write("import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;");
		}
	}

	/**
	 * Write statements created by {@link #addInitStatement}. This code must be
	 * placed after all instantiation code.
	 */
	private void writeInitStatements(IndentedWriter niceWriter) {
		for (String s : initStatements) {
			niceWriter.write(s);
		}
	}

	/**
	 * Write the statements to fill in the fields of the UI owner.
	 */
	private void writeOwnerFieldSetters(IndentedWriter niceWriter) throws UnableToCompleteException {
		if (designTime.isDesignTime()) {
			return;
		}
		for (OwnerField ownerField : getOwnerClass().getUiFields()) {
			String fieldName = ownerField.getName();
			FieldWriter fieldWriter = fieldManager.lookup(fieldName);

			if (fieldWriter != null) {
				// ownerField is a widget.
				JClassType type = fieldWriter.getInstantiableType();
				if (type != null) {
					maybeWriteFieldSetter(niceWriter, ownerField, fieldWriter.getInstantiableType(), fieldName);
				} else {
					// Must be a generated type
					if (!ownerField.isProvided()) {
						niceWriter.write("owner.%1$s = %1$s;", fieldName);
					}
				}

			} else {
				// ownerField was not found as bundle resource or widget, must die.
				die("Template %s has no %s attribute for %s.%s#%s", templatePath, getUiFieldAttributeName(), uiOwnerType.getPackage().getName(), uiOwnerType.getName(),
						fieldName);
			}
		}
	}

	private void writePackage(IndentedWriter w) {
		String packageName = baseClass.getPackage().getName();
		if (packageName.length() > 0) {
			w.write("package %1$s;", packageName);
			w.newline();
		}
	}

	/**
	 * Writes the UiRenderer's source for the renderable strategy.
	 */
	private void writeRenderer(IndentedWriter w, FieldWriter rootField) throws UnableToCompleteException {
		validateRendererGetters(baseClass);
		validateRenderParameters(baseClass);
		JMethod[] eventMethods = findEventMethods(baseClass);
		for (JMethod jMethod : eventMethods) {
			validateEventMethod(jMethod);
		}

		writePackage(w);

		writeImports(w);
		w.newline();

		writeClassOpen(w);
		writeStatics(w);
		w.newline();

		// Create SafeHtml Template
		writeTemplatesInterface(w);
		w.newline();
		htmlTemplates.writeTemplateCallers(w);

		w.newline();

		JParameter[] renderParameters = findRenderParameters(baseClass);
		for (JParameter param : renderParameters) {
			// Prevent fields from render() parameters from being optimized.
			fieldManager.disableOptimization(param.getName());
		}

		// public UiRendererImplClass() {
		w.write("public %s() {", implClassName);
		w.indent();
		w.write("build_fields();");
		w.outdent();
		// }
		w.write("}");
		w.newline();

		// private build_fields() {
		w.write("private void build_fields() {");
		w.indent();
		fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
		w.outdent();
		// }
		w.write("}");
		w.newline();

		String renderParameterDeclarations = renderMethodParameters(renderParameters);
		w.write("public void render(final %s sb%s%s) {", SafeHtmlBuilder.class.getName(), renderParameterDeclarations.length() != 0 ? ", " : "",
				renderParameterDeclarations);
		w.indent();
		w.newline();

		writeRenderParameterInitializers(w, renderParameters);

		w.write("uiId = com.google.gwt.dom.client.Document.get().createUniqueId();");
		w.newline();

		w.write("build_fields();");
		w.newline();

		String safeHtml = rootField.getSafeHtml();

		// TODO(rchandia) it should be possible to add the attribute when parsing
		// the UiBinder file
		w.write("sb.append(stampUiRendererAttribute(%s, RENDERED_ATTRIBUTE, uiId));", safeHtml);
		w.outdent();

		w.write("}");
		w.newline();

		fieldManager.writeFieldDefinitions(w, getOracle(), getOwnerClass(), getDesignTime());

		writeRendererGetters(w, baseClass, rootField.getName());

		writeRendererEventMethods(w, eventMethods, rootField.getName());

		// Close class
		w.outdent();
		w.write("}");
	}

	private void writeRendererDispatcher(IndentedWriter w, String dispatcherName, JClassType targetType, String rootFieldName, JMethod[] uiHandlerMethods,
			JMethod sourceMethod) throws UnableToCompleteException {

		// static class UiRendererDispatcherForFoo extends UiRendererDispatcher<Foo>
		// {
		w.write("static class %s extends UiRendererDispatcher<%s> {", dispatcherName, targetType.getQualifiedSourceName());
		w.indent();

		writeRendererDispatcherTableInit(w, rootFieldName, uiHandlerMethods, dispatcherName);

		writeRendererDispatcherExtraParameters(w, sourceMethod);

		writeRendererDispatcherFire(w, sourceMethod);

		w.write("@SuppressWarnings(\"rawtypes\")");
		w.write("@Override");
		// public void fireEvent(GwtEvent<?>
		// somethingUnlikelyToCollideWithParamNames) {
		w.write("public void fireEvent(com.google.gwt.event.shared.GwtEvent<?> %sEvent) {", SAFE_VAR_PREFIX);
		w.indent();
		// switch (getMethodIndex()) {
		w.write("switch (getMethodIndex()) {");
		w.indent();
		for (int j = 0; j < uiHandlerMethods.length; j++) {
			JMethod uiMethod = uiHandlerMethods[j];

			// case 0:
			w.write("case %s:", j);
			w.indent();

			// getEventTarget().onClickRoot((ClickEvent)
			// somethingUnlikelyToCollideWithParamNames,
			// getRoot(), a, b);
			StringBuffer sb = new StringBuffer();
			JParameter[] sourceParameters = sourceMethod.getParameters();
			// Cat the extra parameters i.e. ", a, b"
			JType[] uiHandlerParameterTypes = uiMethod.getParameterTypes();
			if (uiHandlerParameterTypes.length >= 2) {
				sb.append(", getRoot()");
			}
			for (int k = 2; k < uiHandlerParameterTypes.length; k++) {
				JParameter sourceParam = sourceParameters[k + 1];
				sb.append(", ");
				sb.append(sourceParam.getName());
			}
			w.write("getEventTarget().%s((%s) %sEvent%s);", uiMethod.getName(), uiHandlerParameterTypes[0].getQualifiedSourceName(), SAFE_VAR_PREFIX, sb.toString());
			// break;
			w.write("break;");
			w.newline();
			w.outdent();
		}
		// default:
		w.write("default:");
		w.indent();
		// break;
		w.write("break;");
		w.outdent();
		w.outdent();
		w.write("}");

		w.outdent();
		w.write("}");

		w.outdent();
		w.write("}");
	}

	private void writeRendererDispatcherExtraParameters(IndentedWriter w, JMethod sourceMethod) {
		for (int i = 3; i < sourceMethod.getParameters().length; i++) {
			JParameter param = sourceMethod.getParameters()[i];

			// private int a;
			// private String b;
			w.write("private %s %s;", param.getType().getParameterizedQualifiedSourceName(), param.getName());
		}
	}

	private void writeRendererDispatcherFire(IndentedWriter w, JMethod sourceMethod) {
		// public void fire(Foo o, NativeEvent e, Element parent, int a, String b) {
		w.write("public void fire(");
		w.indent();
		JParameter[] sourceParameters = sourceMethod.getParameters();
		for (int i = 0; i < sourceParameters.length; i++) {
			JParameter param = sourceParameters[i];
			w.write(i == 0 ? "%s %s" : ", %s %s", param.getType().getQualifiedSourceName(), param.getName());
		}
		w.write(") {");
		w.indent();

		// this.a = a;
		for (int i = 3; i < sourceParameters.length; i++) {
			JParameter sourceParam = sourceParameters[i];
			w.write("this.%s = %s;", sourceParam.getName(), sourceParam.getName());
		}

		// fireEvent(o, e, parent);
		w.write("fireEvent(%s, %s, %s);", sourceParameters[0].getName(), sourceParameters[1].getName(), sourceParameters[2].getName());

		w.outdent();
		w.write("}");
		w.newline();
	}

	private void writeRendererDispatcherTableInit(IndentedWriter w, String rootFieldName, JMethod[] uiHandlerMethods, String dispatcherName)
			throws UnableToCompleteException {
		ArrayList<String> keys = new ArrayList<String>();
		ArrayList<Integer> values = new ArrayList<Integer>();

		// Collect the event types and field names to form the dispatch table
		for (int i = 0; i < uiHandlerMethods.length; i++) {
			JMethod jMethod = uiHandlerMethods[i];
			String eventType = findEventTypeName(jMethod);
			String[] fieldNames = jMethod.getAnnotation(UiHandler.class).value();
			for (String fieldName : fieldNames) {
				if (rootFieldName.equals(fieldName)) {
					fieldName = AbstractUiRenderer.ROOT_FAKE_NAME;
				}
				keys.add(eventType + AbstractUiRenderer.UI_ID_SEPARATOR + fieldName);
				values.add(i);
			}
		}

		// private static String[] somethingUnlikelyToCollideWithParamNames_keys;
		w.write("private static String[] %s_keys;", SAFE_VAR_PREFIX);
		// private static Integer[] somethingUnlikelyToCollideWithParamNames_values;
		w.write("private static Integer[] %s_values;", SAFE_VAR_PREFIX);

		w.write("static {");
		w.indent();
		// private static String[] somethingUnlikelyToCollideWithParamNames_keys =
		// new String[] {
		w.write("%s_keys = new String[] {", SAFE_VAR_PREFIX);
		w.indent();
		for (String key : keys) {
			// "click:aField",
			w.write("\"%s\",", key);
		}
		w.outdent();
		w.write("};");
		w.newline();

		// somethingUnlikelyToCollideWithParamNames_values = {0,1};
		w.write("%s_values = new Integer[] {", SAFE_VAR_PREFIX);
		w.indent();
		StringBuffer commaSeparatedValues = new StringBuffer();
		for (Integer value : values) {
			commaSeparatedValues.append(value);
			commaSeparatedValues.append(",");
		}
		// "0,0,0,1,1,",
		w.write("%s", commaSeparatedValues.toString());
		w.outdent();
		w.write("};");
		w.newline();

		w.outdent();
		w.write("}");
		w.newline();

		// public Foo() {
		w.write("public %s() {", dispatcherName);
		w.indent();
		// initDispatchTable(keys, values);
		w.write("initDispatchTable(%s_keys, %s_values);", SAFE_VAR_PREFIX, SAFE_VAR_PREFIX);

		// This ensures the DomEvent#TYPE fields are properly initialized and
		// registered
		// ClickEvent.getType();
		HashSet<String> eventTypes = new HashSet<String>();
		for (JMethod uiMethod : uiHandlerMethods) {
			eventTypes.add(uiMethod.getParameterTypes()[0].getQualifiedSourceName());
		}
		for (String eventType : eventTypes) {
			w.write("%s.getType();", eventType);
		}

		w.outdent();
		w.write("}");
		w.newline();
	}

	private void writeRendererEventMethods(IndentedWriter w, JMethod[] eventMethods, String rootField) throws UnableToCompleteException {
		for (JMethod jMethod : eventMethods) {
			JClassType eventTargetType = jMethod.getParameterTypes()[0].isClassOrInterface();
			String eventTargetSimpleName = eventTargetType.getSimpleSourceName();
			String dispatcherClassName = UI_RENDERER_DISPATCHER_PREFIX + eventTargetSimpleName;
			JMethod[] uiHandlerMethods = findUiHandlerMethods(eventTargetType);

			// public void onBrowserEvent(Foo f, NativeEvent event, Element parent, A
			// a, B b ...) {
			w.write("@Override");
			w.write("public %s {", jMethod.getReadableDeclaration(true, true, true, true, true));

			if (uiHandlerMethods.length != 0) {
				w.indent();
				// if (singletonUiRendererDispatcherForFoo == null) {
				w.write("if (singleton%s == null) {", dispatcherClassName);
				w.indent();
				// singletonUiRendererDispatcherForFoo = new
				// UiRendererDispatcherForFoo();
				w.write("singleton%s = new %s();", dispatcherClassName, dispatcherClassName);

				w.outdent();
				w.write("}");

				// singletonUiRendererDispatcherForFoo.fire(o, event, parent, a, b);
				StringBuffer sb = new StringBuffer();
				JParameter[] parameters = jMethod.getParameters();
				for (int i = 0; i < parameters.length; i++) {
					JParameter callParam = parameters[i];
					if (i != 0) {
						sb.append(", ");
					}
					sb.append(callParam.getName());
				}
				w.write("singleton%s.fire(%s);", dispatcherClassName, sb.toString());
				w.outdent();
			}

			w.write("}");
			w.newline();

			if (uiHandlerMethods.length != 0) {
				// private static UiRendererDispatcherForFoo
				// singletonUiRendererDispatcherForFoo;
				w.write("private static %s singleton%s;", dispatcherClassName, dispatcherClassName);

				writeRendererDispatcher(w, dispatcherClassName, eventTargetType, rootField, uiHandlerMethods, jMethod);
			}
		}
	}

	private void writeRendererGetters(IndentedWriter w, JClassType owner, String rootFieldName) {
		List<JMethod> getters = findGetterNames(owner);

		// For every requested getter
		for (JMethod getter : getters) {
			// public ElementSubclass getFoo(Element parent) {
			w.write("%s {", getter.getReadableDeclaration(false, false, false, false, true));
			w.indent();
			String getterFieldName = getterToFieldName(getter.getName());
			// Is this a CSS resource field?
			FieldWriter fieldWriter = fieldManager.lookup(getterFieldName);
			if (FieldWriterType.GENERATED_CSS.equals(fieldWriter.getFieldType())) {
				// return (CssResourceSubclass) get_styleField;
				w.write("return (%s) %s;", getter.getReturnType().getErasedType().getQualifiedSourceName(), FieldManager.getFieldGetter(getterFieldName));
			} else {
				// Else the non-root elements are found by id
				String elementParameter = getter.getParameters()[0].getName();
				if (!getterFieldName.equals(rootFieldName)) {
					// return (ElementSubclass) findInnerField(parent, "foo",
					// RENDERED_ATTRIBUTE);
					w.write("return (%s) findInnerField(%s, \"%s\", RENDERED_ATTRIBUTE);", getter.getReturnType().getErasedType().getQualifiedSourceName(),
							elementParameter, getterFieldName);
				} else {
					// return (ElementSubclass) findRootElement(parent);
					w.write("return (%s) findRootElement(%s, RENDERED_ATTRIBUTE);", getter.getReturnType().getErasedType().getQualifiedSourceName(), elementParameter);
				}
			}
			w.outdent();
			w.write("}");
		}
	}

	private void writeRenderParameterInitializers(IndentedWriter w, JParameter[] renderParameters) {
		for (int i = 0; i < renderParameters.length; i++) {
			JParameter parameter = renderParameters[i];
			if (fieldManager.lookup(parameter.getName()) != null) {
				w.write("this.%s = %s;", parameter.getName(), parameter.getName());
				w.newline();
			}
		}
	}

	private void writeStaticMessagesInstance(IndentedWriter niceWriter) {
		if (messages.hasMessages()) {
			niceWriter.write(messages.getDeclaration());
		}
	}

	private void writeStatics(IndentedWriter w) {
		writeStaticMessagesInstance(w);
		designTime.addDeclarations(w);
	}

	/**
	 * Write statements created by {@link HtmlTemplatesWriter#addSafeHtmlTemplate}
	 * . This code must be placed after all instantiation code.
	 */
	private void writeTemplatesInterface(IndentedWriter w) {
		if (!(htmlTemplates.isEmpty())) {
			assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
			htmlTemplates.writeInterface(w);
			w.newline();
		}
	}
}
